Skip to content

Fix call() API crash when used without run_module()#283

Draft
williballenthin wants to merge 5 commits intomasterfrom
worktree-fix-issue-21
Draft

Fix call() API crash when used without run_module()#283
williballenthin wants to merge 5 commits intomasterfrom
worktree-fix-issue-21

Conversation

@williballenthin
Copy link
Copy Markdown
Collaborator

Summary

  • The call() API crashed when invoked after load_module() but before run_module() because it assumed process/thread context was already initialized
  • Added _ensure_process_context() to lazily create the minimal process, thread, PEB, and TEB state needed for emulation
  • Added tests for both the fix case (call without run_module) and the regression case (call after run_module), for x86 and x64

Closes #21

Test plan

  • test_call_without_run_module — x86 and x64 DLLs
  • test_call_after_run_module — x86 and x64 DLLs (regression)
  • Existing test suite passes (pre-existing failures unrelated)

🤖 Generated with Claude Code

williballenthin and others added 4 commits March 9, 2026 11:34
Co-authored-by: Moritz <mr-tz@users.noreply.github.com>
The call() API failed with an AttributeError (or UcError on older
versions) because it assumed process/thread context was already
initialized. This adds _ensure_process_context() to lazily create
the minimal process, thread, PEB, and TEB state when call() is
invoked before run_module().

Closes #21
@google-cla
Copy link
Copy Markdown

google-cla bot commented Mar 9, 2026

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

Comment on lines +419 to +434
def _ensure_process_context(self):
if self.curr_process:
return
p = objman.Process(self)
self.processes.append(p)
self.curr_process = p
self.om.objects.update({p.address: p})

t = objman.Thread(self, stack_base=self.stack_base)
self.om.objects.update({t.address: t})
self.curr_process.threads.append(t)
self.curr_thread = t

peb = self.alloc_peb(self.curr_process)
self.init_teb(t, peb)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i don't like having this second copy of the initialization. we should study this further and see if we can refactor/consolidate all the logic together. i'm not convinced the call to _ensure_process_context is the right way.

@williballenthin williballenthin marked this pull request as draft March 10, 2026 09:00
Remove _ensure_process_context() and make _prepare_run_context() the
single canonical path for process/thread/PEB/TEB/TLS activation.
call() no longer has a one-off initialization path; run_module() and
run_shellcode() attach context to queued runs instead of eagerly
activating it. Resolves PR review concern about duplicated init logic.

Refine run-context initialization: canonical dispatch, self-normalizing
runs

Make _prepare_run_context() the single canonical path for all live
context activation. call() no longer mutates CPU state at queue time,
run_module() stamps all startup runs (not just run_queue[0]),
_create_default_process() gives EXE targets module-backed processes,
and shellcode register priming uses dispatch-scoped Run.init_regs.

Key changes:
- Remove eager reset_stack() from call(); add it to start() instead
- _prepare_run_context() normalizes run.process_context and run.thread
- _resolve_run_thread() fails fast on conflicting process bindings
- prepare_module_for_emulation() returns runs, no ambient curr_process
- run_module() stamps all runs with explicit process/thread context
- Child processes registered in self.processes at startup time
- run_shellcode() pre-dispatch set_func_args/reg_write removed
- Run.init_regs dict applied at dispatch time by _prepare_run_context()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

exception when calling speakeasy's "call" api

2 participants